/* eslint-disable @typescript-eslint/no-explicit-any */

import SVGSearch from '@mdi/svg/svg/magnify.svg';
import { DICT } from '../../dict/dict';
// import SVGSearch from '../../svg/search.svg';
import type { GridConfig } from '../grid/grid';
import { IconInput } from '../input/input';
import { type ColumnOption, getText, getValue, List, type SelectType } from '../list/list';
import type { PopupPosition, Pos } from '../popup/popup';
import { Selector } from '../selector/selector';
import { popListStyle } from './popList.css';

function getRowText(row: PopListRowType, col: ColumnOption) {
	const valueKey = col.text ?? 'text';
	if (valueKey === undefined || row === undefined || row === null || typeof row !== 'object') {
		return row;
	} else {
		return (row as any)[valueKey];
	}
}

function getRowChildren(row: PopListRowType, col: ColumnOption): PopListRowType[] | null {
	const childrenKey = col.children;
	if (childrenKey === undefined || row === undefined || row === null || typeof row !== 'object') {
		return null;
	} else {
		return (row as any)[childrenKey];
	}
}

export type PopListRowType = Record<string | number, unknown> | unknown;

function filterData(data: PopListRowType[], opt: ColumnOption, keyword: string): PopListRowType[] {
	const result: PopListRowType[] = [];
	function iterate(rows: PopListRowType[]) {
		rows.forEach((row) => {
			const children = getRowChildren(row, opt);
			const t = getRowText(row, opt);
			if (t !== undefined && t !== null) {
				if ((t + '').includes(keyword)) {
					result.push(row);
				}
			}
			if (children) iterate(children);
		});
	}
	iterate(data);
	return result;
}

export type PopListOptions = {
	refer?: HTMLElement;
	columnOption?: ColumnOption;
	focusOnClose?: HTMLElement;
	multiSelect?: boolean;
	multiSelectType?: SelectType;
	selected?: unknown[];
	activeValue?: unknown;
	useAction?: boolean;
	useSearch?: boolean;
	maxShowLines?: number;
	focus?: boolean;
	popupPosition?: PopupPosition | Pos;
	width?: number | string;
	minWidth?: number | string;
	maxWidth?: number | string;
	disableField?: number | string | null;
	noSelectField?: number | string | null;
	/**
	 * Instead returning the text field of the selected row, you can specify other field as the returned text.
	 */
	resultTextField?: number | string | null;
	/**
	 * Whether to return the full path of the selection, which can be a boolean or a string. If it is a string, it is considered that the full path is returned, and the specified string is used as the path separator.
	 */
	returnFullPath?: boolean | string;
	gridConfig?: Partial<GridConfig>;
	highlighHover?: boolean;
};

export interface PopListSelectHandler {
	list: List;
	close(): void;
	onSelect(callback: PopListSelectCallback): this;
	onActiveRow(callback: (row: PopListRowType, index: number) => void): this;
	onSelectMultiple(callback: PopListSelectMultipleCallback): this;
	onClose(callback: (esc: boolean) => void): this;
	onWillClose(callback: (esc: boolean) => void): this;
}

function getFullPath(
	data: PopListRowType[],
	row: PopListRowType,
	col: ColumnOption,
	splitter: string,
): string | null {
	for (const v of data) {
		if (v === row) {
			return getRowText(v, col);
		} else {
			const children = getRowChildren(v, col);
			if (children) {
				const childText = getFullPath(children, row, col, splitter);
				if (childText != null) {
					return getRowText(v, col) + splitter + childText;
				}
			}
		}
	}
	return null;
}

export type PopListSelectCallback = (value: unknown, text: string, row: number) => void;
export type PopListSelectMultipleCallback = (selected: { value: unknown; text: string }[]) => void;

export const PopList = {
	show(
		data: PopListRowType[],
		{
			refer,
			columnOption = {},
			focusOnClose,
			activeValue,
			multiSelect = false,
			multiSelectType = 'any',
			selected,
			useAction = false,
			useSearch = false,
			/**
			 * Whether to focus the list box when the popup is opened.
			 */
			focus = true,
			maxShowLines = 8,
			popupPosition = 'bottom-left',
			gridConfig,
			width,
			minWidth,
			maxWidth,
			disableField,
			noSelectField,
			resultTextField,
			returnFullPath,
			highlighHover = true,
		}: PopListOptions = {},
	): PopListSelectHandler {
		columnOption.text = columnOption.text ?? 'text';
		columnOption.value = columnOption.value ?? 'value';
		let onClose: ((esc: boolean) => void) | undefined;
		let onWillClose: ((esc: boolean) => void) | undefined;
		let onSelect: PopListSelectCallback | undefined;
		let onSelectMultiple: PopListSelectMultipleCallback | undefined;
		let onActiveRow: ((row: PopListRowType, index: number) => void) | undefined;
		const selectRow = () => {
			const activeRow = l.active;
			if (activeRow !== null) {
				if (resultTextField !== undefined && resultTextField !== null) {
					const row = l.activeRow as any;
					onSelect?.(l.activeValue, row?.[resultTextField], activeRow);
				} else if (returnFullPath) {
					const row = l.activeRow as any;
					const fullPath = getFullPath(
						data,
						row,
						columnOption,
						typeof returnFullPath === 'string' ? returnFullPath : ' / ',
					);
					onSelect?.(l.activeValue, fullPath + '', activeRow);
				} else {
					onSelect?.(l.activeValue, l.activeText ?? '', activeRow);
				}
			}
			pop.close();
			if (focusOnClose) {
				focusOnClose.focus();
			}
		};
		const selectMultiple = () => {
			// process multiple selection
			onSelectMultiple?.(
				l.selected.map((row: any) => {
					if (resultTextField !== undefined && resultTextField !== null) {
						return { value: getValue(row, columnOption), text: row?.[resultTextField] };
					} else if (returnFullPath) {
						const fullPath = getFullPath(
							data,
							row,
							columnOption,
							typeof returnFullPath === 'string' ? returnFullPath : ' / ',
						);
						return { value: getValue(row, columnOption), text: fullPath };
					} else {
						return { value: getValue(row, columnOption), text: getText(row, columnOption) };
					}
				}),
			);
			pop.close();
			if (focusOnClose) {
				focusOnClose.focus();
			}
		};

		const l: List = new List({
			class: [...(highlighHover ? ['highligh-hover'] : [])],
			column: { ...columnOption, nonSelectableField: noSelectField ?? undefined },
			rows: data,
			activable: true, //!multiSelect,
			selectable: multiSelect,
			selectType: multiSelectType,
			showEmpty: false,
			autoHeight: true,
			disableOn: disableField ?? null,
			style: {
				border: 'none',
				borderRadius: '0',
				// width: this.innerWidth() + 'px',
			},
			onRowClick: (detail) => {
				if (multiSelect) return;
				if (
					noSelectField !== undefined &&
					noSelectField !== null &&
					!!(detail.row as any)?.row?.[noSelectField]
				) {
					return;
				}
				selectRow();
			},
			onRowPress: (detail, evt) => {
				if (detail.key === 'Enter') {
					evt.preventDefault();
					evt.stopPropagation();
					evt.stopImmediatePropagation();
					if (multiSelect) {
						selectMultiple();
					} else {
						if (
							noSelectField !== undefined &&
							noSelectField !== null &&
							!!(detail.row as any)?.row?.[noSelectField]
						) {
							return;
						}
						selectRow();
					}
				}
			},
			onRowActive: (detail) => {
				onActiveRow?.(detail.row as any, detail.rowIndex);
			},
		});

		if (multiSelect && selected) {
			l.setSelected(selected);
		}
		if (gridConfig) {
			l.setConfig(gridConfig);
		}

		function createSearch() {
			const searchOpt = { ...columnOption };
			searchOpt.children = undefined;
			const ipt = new IconInput({
				icon: SVGSearch,
				placeholder: DICT.search,
				showClear: true,
				onKeyDown: (e) => {
					if (e.key === 'ArrowDown') {
						l.focus();
						if (l.active === null) {
							l.setActive(0);
						}
						e.stopPropagation();
						e.preventDefault();
					}
				},
				onClear: () => {
					l.setRows(data, false);
					l.setColumn(columnOption);
				},
				onInput: () => {
					const keyword = (ipt.value + '').trim();
					if (keyword) {
						if (searchOpt != l.column) {
							l.setRows(filterData(data, columnOption, keyword), false);
							l.setColumn(searchOpt);
						} else {
							l.setRows(filterData(data, columnOption, keyword));
						}
					} else {
						l.setRows(data, false);
						l.setColumn(columnOption);
					}
				},
			});
			return ipt;
		}
		const container = (
			<div class={popListStyle}>
				{useSearch ? createSearch().el : ''}
				{l.el}
			</div>
		) as HTMLDivElement;

		const pop = Selector.show(container, {
			refer,
			focusOnClose,
			popupPosition,
			focus: false,
			useAction,
			width,
			minWidth,
			maxWidth,
			noActionBar: !multiSelect,
		})
			.onOpen(() => {
				l.el.style.maxHeight = l.getLineHeight() * maxShowLines + 'px';
				l.refresh();
				if (activeValue !== undefined) {
					l.setActiveValue(activeValue, true);
				}
				if (focus) l.focus();
			})
			.onClose((esc) => {
				onClose?.(esc);
			})
			.onWillClose((esc) => {
				onWillClose?.(esc);
			})
			.onOK(() => {
				selectMultiple();
			});
		return {
			list: l,
			close() {
				pop.close();
			},
			onSelect(callback) {
				onSelect = callback;
				return this;
			},
			onSelectMultiple(multipleCallback) {
				onSelectMultiple = multipleCallback;
				return this;
			},
			onActiveRow(callback) {
				onActiveRow = callback;
				return this;
			},
			onClose(callback) {
				onClose = callback;
				return this;
			},
			onWillClose(callback) {
				onWillClose = callback;
				return this;
			},
		};
	},
};
